home *** CD-ROM | disk | FTP | other *** search
/ NeXT Education Software Sampler 1992 Fall / NeXT Education Software Sampler 1992 Fall.iso / Programming / Classes / 2.0_NumberLine / NumberLine.m < prev    next >
Encoding:
Text File  |  1992-06-08  |  17.3 KB  |  662 lines

  1. // By Judy D. Halchin, Educational Computing Services, Allegheny College.
  2. // You may freely copy, distribute and reuse this code. 
  3. // Allegheny College and the author disclaim any warranty of any kind, 
  4. // expressed or implied, as to its fitness for any particular use.
  5. // This work was partially supported by a Teacher Preparation grant from the 
  6. // National Science Foundation.
  7.  
  8. #import "NumberLine.h"
  9. #import "drawLine.h"
  10. #import <appkit/NXImage.h>
  11. #import <appkit/graphics.h>
  12. #import <dpsclient/wraps.h>
  13. #import <appkit/nextstd.h>
  14.  
  15. @implementation NumberLine
  16.  
  17. float mantissa(float floatValue);
  18. int   orderOfMag(float floatValue);
  19. int   round(float floatValue);
  20.  
  21. - initFrame:(NXRect *)frameRect
  22. {
  23.     [super initFrame:frameRect];
  24.     
  25.     //initialize all instance variables
  26.       linePosition = 30;
  27.       if (bounds.size.width >= bounds.size.height)
  28.             orientation = HORIZONTAL;
  29.       else
  30.             orientation = VERTICAL;
  31.       labelPosition = BELOWLEFT;
  32.       
  33.       backgroundGray = NX_WHITE;
  34.       lineGray = NX_BLACK;
  35.       labelGray = NX_BLACK;
  36.     
  37.       borderWidth = 0;
  38.       endBars = NO;
  39.       
  40.     //make an NXImage to hold the number line
  41.       numberLineNXImage = [[NXImage allocFromZone:[self zone]]     
  42.                                                   initSize:&bounds.size];
  43.       
  44.     //if orientation is vertical, rotate to match the number line 
  45.       if (orientation == VERTICAL)
  46.       {
  47.             [self rotate:90];
  48.           [self translate:0 :-bounds.size.height];
  49.       }
  50.  
  51.     //scale and translate                        
  52.       [self scale:bounds.size.width/20 :1];
  53.       [self translate:10 :linePosition];
  54.  
  55.     //calculate the tick marks and draw the line in the NXImage
  56.       [self calculateTicks];
  57.       [self drawIntoImage];
  58.     
  59.     return self;
  60. }
  61.  
  62. - drawSelf:(const NXRect *)rects :(int)rectCount
  63. {
  64.     NXPoint compositePoint;
  65.     
  66.     compositePoint.x = bounds.origin.x;
  67.     if (orientation == HORIZONTAL)
  68.         compositePoint.y = bounds.origin.y;
  69.     else
  70.         compositePoint.y = bounds.origin.y + bounds.size.height;
  71.     PSsetgray(1.0);
  72.     NXRectFill(&bounds);
  73.     [numberLineNXImage composite:NX_COPY toPoint:&compositePoint];
  74.     return self;
  75. }
  76.  
  77. - drawIntoImage
  78. {
  79.     NXSize imageSize;
  80.     float scaleFactor, unscaleFactor, length;
  81.     
  82.     [numberLineNXImage getSize:&imageSize];
  83.     length = MAX(imageSize.width, imageSize.height);
  84.     scaleFactor = length / bounds.size.width;
  85.     unscaleFactor = bounds.size.width / length;
  86.     
  87.     [numberLineNXImage lockFocus];
  88.  
  89.     drawBorders(imageSize.width, imageSize.height, linePosition, borderWidth,
  90.                 backgroundGray, lineGray, orientation, endBars);
  91.                         
  92.      drawLine(bounds.origin.x, bounds.origin.y, bounds.size.width, 
  93.                 bounds.size.height, scaleFactor, orientation, lineGray);
  94.                 
  95.     drawTicks(bounds.origin.x, bounds.origin.y, bounds.size.height, 
  96.                 scaleFactor, orientation, lineGray, tickInfo.firstTick, 
  97.                 tickInfo.tickSpacing, tickInfo.firstLabel, 
  98.                 tickInfo.labelSpacing, tickInfo.numberOfTicks);
  99.               
  100.     if (orientation == HORIZONTAL)
  101.         drawHorzLabels(bounds.origin.x, bounds.origin.y, scaleFactor, 
  102.                     unscaleFactor, labelPosition, labelGray, 
  103.                     tickInfo.firstLabel, tickInfo.labelSpacing, 
  104.                     tickInfo.scientific, tickInfo.numberOfLabels, 
  105.                     tickInfo.labelString[0], 
  106.                     tickInfo.labelString[1], tickInfo.labelString[2], 
  107.                     tickInfo.labelString[3], tickInfo.labelString[4], 
  108.                     tickInfo.labelString[5], tickInfo.labelString[6], 
  109.                     tickInfo.labelString[7], tickInfo.labelString[8], 
  110.                     tickInfo.labelString[9], tickInfo.labelString[10], 
  111.                     tickInfo.expString[0], tickInfo.expString[1], 
  112.                     tickInfo.expString[2], tickInfo.expString[3], 
  113.                     tickInfo.expString[4], tickInfo.expString[5], 
  114.                     tickInfo.expString[6], tickInfo.expString[7], 
  115.                     tickInfo.expString[8], tickInfo.expString[9], 
  116.                     tickInfo.expString[10]);
  117.      else
  118.         drawVertLabels(bounds.origin.x, bounds.origin.y, bounds.size.height, 
  119.                     scaleFactor, unscaleFactor, labelPosition, labelGray, 
  120.                     tickInfo.firstLabel, tickInfo.labelSpacing, 
  121.                     tickInfo.scientific, tickInfo.numberOfLabels, 
  122.                     tickInfo.labelString[0], 
  123.                     tickInfo.labelString[1], tickInfo.labelString[2], 
  124.                     tickInfo.labelString[3], tickInfo.labelString[4], 
  125.                     tickInfo.labelString[5], tickInfo.labelString[6], 
  126.                     tickInfo.labelString[7], tickInfo.labelString[8], 
  127.                     tickInfo.labelString[9], tickInfo.labelString[10], 
  128.                     tickInfo.expString[0], tickInfo.expString[1], 
  129.                     tickInfo.expString[2], tickInfo.expString[3], 
  130.                     tickInfo.expString[4], tickInfo.expString[5], 
  131.                     tickInfo.expString[6], tickInfo.expString[7], 
  132.                     tickInfo.expString[8], tickInfo.expString[9], 
  133.                     tickInfo.expString[10]);
  134.     
  135.     [numberLineNXImage unlockFocus];
  136.  
  137.     return self;
  138. }
  139.  
  140. - numberLineNXImage
  141. {
  142.     return numberLineNXImage;
  143. }
  144.     
  145. - (float)max
  146. {
  147.     return bounds.origin.x + bounds.size.width;
  148. }
  149.  
  150. - (float)min
  151. {
  152.     return bounds.origin.x;
  153. }
  154.  
  155. - setMin:(float)newMin max:(float)newMax
  156. {
  157.     //return if input is invalid
  158.       if (newMin >= newMax)
  159.           return self;
  160.  
  161.     //perform the translation and rotation
  162.       [super scale:bounds.size.width/(newMax-newMin) :1];
  163.       [super translate:bounds.origin.x - newMin :0];
  164.             
  165.     //recalculate ticks and redraw into NXImage
  166.       [self calculateTicks];
  167.       [self drawIntoImage];
  168.         
  169.     //display if we're supposed to
  170.       if (!vFlags.disableAutodisplay)
  171.             [self display];
  172.           
  173.     return self;
  174. }
  175.  
  176. - zoomIn:(float)amount
  177. {
  178.     float center, halfRange;
  179.     
  180.       if (amount > 0)
  181.     {
  182.         center = bounds.origin.x + bounds.size.width/2;
  183.         halfRange = bounds.size.width/amount/2;
  184.         [self setMin:center - halfRange max:center + halfRange];
  185.     }
  186.     return self;
  187. }
  188.  
  189. - zoomOut:(float)amount
  190. {
  191.     float center, halfRange;
  192.     
  193.       if (amount > 0)
  194.     {
  195.         center = bounds.origin.x + bounds.size.width/2;
  196.         halfRange = bounds.size.width*amount/2;
  197.         [self setMin:center - halfRange max:center + halfRange];
  198.     }
  199.     return self;
  200. }
  201.  
  202. - (float)center
  203. {
  204.     return bounds.origin.x + bounds.size.width / 2;
  205. }
  206.  
  207. - setCenter:(float)newCenter
  208. {
  209.     float translation;
  210.     
  211.     translation = newCenter - (bounds.origin.x + bounds.size.width / 2);
  212.     [self setMin:bounds.origin.x + translation 
  213.              max:bounds.origin.x + bounds.size.width + translation];
  214.     return self;
  215. }
  216.  
  217. - slide:(float)translation
  218. {
  219.     [self setMin:bounds.origin.x + translation 
  220.              max:bounds.origin.x + bounds.size.width + translation];
  221.     return self;
  222. }
  223.  
  224. - (float)linePosition
  225. {
  226.     return linePosition;
  227. }
  228.  
  229. - setLinePosition:(float)newPosition
  230. {
  231.     [self translate:0 :newPosition - linePosition];
  232.     linePosition = newPosition;
  233.     [self drawIntoImage];
  234.     return self;
  235. }
  236.  
  237. - setBackgroundGray:(float)gray
  238. {
  239.     backgroundGray = gray;
  240.     [self drawIntoImage];
  241.     return self;
  242. }
  243.  
  244. - setLineGray:(float)gray
  245. {
  246.     lineGray = gray;
  247.     [self drawIntoImage];
  248.     return self;
  249. }
  250.  
  251. - setLabelGray:(float)gray
  252. {
  253.     labelGray = gray;
  254.     [self drawIntoImage];
  255.     return self;
  256. }
  257.  
  258. - (float)backgroundGray
  259. {
  260.     return backgroundGray;
  261. }
  262.  
  263. - (float)lineGray
  264. {
  265.     return lineGray;
  266. }
  267.  
  268. - (float)labelGray
  269. {
  270.     return labelGray;
  271. }
  272.  
  273.  
  274. - (int)orientation
  275. {
  276.     return orientation;
  277. }
  278.  
  279. - (int)labelPosition
  280. {
  281.     return labelPosition;
  282. }
  283.  
  284. - setLabelPosition:(int)newPosition
  285. {
  286.     labelPosition = newPosition;
  287.     return self;
  288. }
  289.  
  290. - setBordered:(BOOL)shouldHaveBorder width:(float)width
  291. {
  292.     if (shouldHaveBorder)
  293.         borderWidth = width;
  294.     else
  295.         borderWidth = -1;
  296.     [self drawIntoImage];
  297.     return self;
  298. }
  299.  
  300. - (BOOL)bordered
  301. {
  302.      if (borderWidth == -1)
  303.         return NO;
  304.     else
  305.         return YES;
  306. }        
  307.     
  308. - setEndBars:(BOOL)shouldHaveEndBars
  309. {
  310.     endBars = shouldHaveEndBars;
  311.     [self drawIntoImage];
  312.     return self;
  313. }
  314.     
  315. - (BOOL)hasEndBars
  316. {
  317.     return endBars;
  318. }
  319.  
  320. - calculateTicks
  321. {
  322.     float rawValue, lastLabel, labelValue, tick, length;
  323.     int orderSpacing, orderFirstLast, maxOrder;
  324.     int labelsGoal, spaceAvail, digits, digitsRight=0, i, nonblank;
  325.     char formatString[20], rawLabel[30], *ePointer;
  326.     BOOL spacing25, labelsOK;
  327.     NXSize imageSize;
  328.     
  329.     //calulate the ideal number of labels desired
  330.       [numberLineNXImage getSize:&imageSize];
  331.       length = MAX(imageSize.width, imageSize.height);
  332.       labelsGoal = (int)(length / 100);
  333.       if (labelsGoal < 3)
  334.           labelsGoal = 3;
  335.     
  336.     //propose a label spacing
  337.       rawValue = mantissa(bounds.size.width) / labelsGoal;
  338.       
  339.       spacing25 = NO;
  340.       if (rawValue <= 0.128)
  341.             tickInfo.labelSpacing = pow(10, orderOfMag(bounds.size.width)-1);
  342.       else if ((0.128 < rawValue) && (rawValue <= 0.25))
  343.             tickInfo.labelSpacing = 2 * pow(10, orderOfMag(bounds.size.width)-1);
  344.       else if ((0.25 < rawValue) && (rawValue <= 0.35))
  345.       {
  346.             tickInfo.labelSpacing = 2.5 *pow(10,orderOfMag(bounds.size.width)-1);
  347.           spacing25 = YES;
  348.       }
  349.       else if ((0.35 < rawValue) && (rawValue <= 0.65))
  350.             tickInfo.labelSpacing = 5 * pow(10, orderOfMag(bounds.size.width)-1);
  351.       else if ((0.65 < rawValue) && (rawValue <= 1.23))
  352.             tickInfo.labelSpacing = pow(10, orderOfMag(bounds.size.width));
  353.       else if ((1.23 < rawValue) && (rawValue <= 2.5))
  354.             tickInfo.labelSpacing = 2 * pow(10, orderOfMag(bounds.size.width));
  355.       else if (2.5 < rawValue)
  356.       {
  357.             tickInfo.labelSpacing = 2.5 * pow(10, orderOfMag(bounds.size.width));
  358.           spacing25 = YES;
  359.       }
  360.                
  361.     //count the labels the proposed spacing will produce and adjust if nec.
  362.       labelsOK = NO;
  363.       while (!labelsOK)
  364.       {
  365.         //find the first and last labels that will be used and number of labels
  366.           //pick a tentative first label and last label
  367.             tickInfo.firstLabel = floor(bounds.origin.x/tickInfo.labelSpacing) 
  368.                                         * tickInfo.labelSpacing;
  369.             if (tickInfo.firstLabel <= bounds.origin.x)
  370.                 tickInfo.firstLabel = tickInfo.firstLabel + 
  371.                                                 tickInfo.labelSpacing;
  372.             for (lastLabel = tickInfo.firstLabel; 
  373.                  lastLabel + tickInfo.labelSpacing < 
  374.                                      bounds.origin.x + bounds.size.width; 
  375.                                   lastLabel = lastLabel + tickInfo.labelSpacing);
  376.               if (orderOfMag(lastLabel) < orderOfMag(tickInfo.firstLabel) - 2) 
  377.             {
  378.                 orderSpacing = orderOfMag(tickInfo.labelSpacing);
  379.                  lastLabel = round(lastLabel * pow(10, -orderSpacing+2)) *
  380.                                                pow(10, orderSpacing-2);        
  381.             }                        
  382.           //determine number of digits and whether to use scientific notation
  383.             orderSpacing = orderOfMag(tickInfo.labelSpacing);
  384.             if (abs(orderOfMag(tickInfo.firstLabel)) >=     
  385.                                                 abs(orderOfMag(lastLabel)))
  386.                   orderFirstLast = orderOfMag(tickInfo.firstLabel);
  387.             else
  388.                   orderFirstLast = orderOfMag(lastLabel);
  389.           
  390.             if ((abs(orderSpacing) > 3) || (abs(orderFirstLast) > 3))
  391.                  tickInfo.scientific = YES;
  392.             else
  393.             {
  394.                   digits = orderFirstLast - orderSpacing;
  395.                 if (spacing25)
  396.                      digits = digits + 1;
  397.                 if (digits > 3)
  398.                       tickInfo.scientific = YES;
  399.                 else
  400.                       tickInfo.scientific = NO;
  401.             }
  402.           
  403.             if (tickInfo.scientific)
  404.             {
  405.                   digits = orderFirstLast - orderSpacing;
  406.                 if (spacing25)
  407.                      digits = digits + 1;
  408.             }
  409.             else
  410.             {
  411.                   maxOrder = MAX(abs(orderFirstLast), abs(orderSpacing));
  412.  
  413.                 if ((orderFirstLast > 0) && (orderSpacing < 0))
  414.                 {
  415.                       digits = orderFirstLast - orderSpacing;
  416.                     digitsRight = - orderSpacing;
  417.                     if (spacing25)
  418.                     {
  419.                           digits = digits + 1;
  420.                           digitsRight = digitsRight + 1;
  421.                     }
  422.                 }
  423.                 else
  424.                 {
  425.                     digits = maxOrder;
  426.                     if ((orderSpacing <= 0) && spacing25)
  427.                           digits = digits + 1;
  428.                     if (orderFirstLast > 0)
  429.                         digitsRight = 0;
  430.                     else
  431.                         digitsRight = maxOrder;
  432.                     if (spacing25 && (orderSpacing <= 0))
  433.                           digitsRight = digitsRight + 1;
  434.                 }
  435.             }
  436.  
  437.           //adjust first and/or last label if there isn't enough space
  438.             spaceAvail = (tickInfo.firstLabel - bounds.origin.x) * length 
  439.                                                 / bounds.size.width;
  440.             if (orientation == HORIZONTAL)
  441.             {
  442.                 if ((tickInfo.scientific && (spaceAvail < (8+digits)*4)))
  443.                     tickInfo.firstLabel = tickInfo.firstLabel + 
  444.                                                 tickInfo.labelSpacing;
  445.                 if ((!tickInfo.scientific) && (spaceAvail < (2+digits)*4))
  446.                     tickInfo.firstLabel = tickInfo.firstLabel + 
  447.                                                 tickInfo.labelSpacing;
  448.             }
  449.             else
  450.                 if (spaceAvail < 6)
  451.                     tickInfo.firstLabel = tickInfo.firstLabel + 
  452.                                                 tickInfo.labelSpacing;
  453.             
  454.             spaceAvail = (bounds.origin.x + bounds.size.width - lastLabel) 
  455.                                                 * length / bounds.size.width;
  456.  
  457.             if (orientation == HORIZONTAL)
  458.             {
  459.                 if (tickInfo.scientific && (spaceAvail < (8+digits)*4))
  460.                     lastLabel = lastLabel - tickInfo.labelSpacing;
  461.                 if ((!tickInfo.scientific) && (spaceAvail < (2+digits)*4))
  462.                     lastLabel = lastLabel - tickInfo.labelSpacing;
  463.             }
  464.             else
  465.                 if (spaceAvail < 10)
  466.                     lastLabel = lastLabel - tickInfo.labelSpacing;
  467.     
  468.           //count the labels
  469.             tickInfo.numberOfLabels = round((lastLabel - 
  470.                             tickInfo.firstLabel)/tickInfo.labelSpacing) + 1;
  471.  
  472.       //alter the spacing if the number of labels is not suitable
  473.         if (tickInfo.numberOfLabels >= 3)
  474.               labelsOK = YES;
  475.         else
  476.         {
  477.               if (fabs(mantissa(tickInfo.labelSpacing) - 1) < 0.01)
  478.                tickInfo.labelSpacing = 5 * 
  479.                                    pow(10, orderOfMag(tickInfo.labelSpacing)-1);
  480.               else if (fabs(mantissa(tickInfo.labelSpacing) - 2) < 0.01)
  481.                tickInfo.labelSpacing = 
  482.                                    pow(10, orderOfMag(tickInfo.labelSpacing));
  483.               else if (fabs(mantissa(tickInfo.labelSpacing) - 2.5) < 0.01)
  484.             {
  485.                tickInfo.labelSpacing = 2 * 
  486.                                    pow(10, orderOfMag(tickInfo.labelSpacing));
  487.                spacing25 = NO;
  488.             }
  489.               else if (fabs(mantissa(tickInfo.labelSpacing) - 5) < 0.01)
  490.             {
  491.                tickInfo.labelSpacing = 2.5 * 
  492.                                    pow(10, orderOfMag(tickInfo.labelSpacing));
  493.                spacing25 = YES;
  494.             }
  495.         }
  496.       }   // end of labelsOK loop
  497.           
  498.     //generate the list of formatted labels
  499.  
  500.       labelValue = tickInfo.firstLabel;
  501.       for (i = 0; i < tickInfo.numberOfLabels; i++)
  502.       {
  503.           //put the value into a string
  504.             if (fabs(labelValue) < tickInfo.labelSpacing * 0.01)
  505.                 labelValue = 0;
  506.             if (tickInfo.scientific)
  507.             {    
  508.                 //put value into string
  509.                   sprintf(formatString, "%%25.%de", digits);
  510.                   sprintf(rawLabel, formatString, labelValue);
  511.                 //delete e and put exponent into separate string
  512.                   ePointer = strchr(rawLabel, 'e');
  513.                   if (*(ePointer+1) == '+')
  514.                         if (*(ePointer+2) == '0')
  515.                             strcpy(tickInfo.expString[i], ePointer+3);
  516.                       else
  517.                             strcpy(tickInfo.expString[i], ePointer+2);
  518.                   else
  519.                   {      
  520.                         strcpy(tickInfo.expString[i], ePointer+1); 
  521.                         if (*(ePointer+2) == '0')
  522.                             strcpy(tickInfo.expString[i]+1, ePointer+3);
  523.                   }
  524.                    *ePointer = '\0';
  525.             }      
  526.             else
  527.             {                 
  528.                   sprintf(formatString, "%%25.%df", digitsRight);
  529.                 sprintf(rawLabel, formatString, labelValue);
  530.             } 
  531.               
  532.           //strip off any leading blanks
  533.             nonblank = strspn(rawLabel, " ");
  534.             strcpy(tickInfo.labelString[i], rawLabel + nonblank);
  535.             
  536.           //don't use decimals with zero
  537.             if (strspn(tickInfo.labelString[i], "0.") == 
  538.                                             strlen(tickInfo.labelString[i]))
  539.                 strcpy(tickInfo.labelString[i], "0");
  540.                 
  541.           //increment value of the label
  542.               labelValue = labelValue + tickInfo.labelSpacing;
  543.       }    
  544.     
  545.     //set the ticks
  546.       tickInfo.tickSpacing = tickInfo.labelSpacing / 5;
  547.       for (tickInfo.firstTick = tickInfo.firstLabel, 
  548.                           tickInfo.numberOfTicks = 0; 
  549.                   tickInfo.firstTick - tickInfo.tickSpacing > bounds.origin.x; 
  550.                   tickInfo.firstTick = tickInfo.firstTick - tickInfo.tickSpacing, 
  551.                                         tickInfo.numberOfTicks++);    
  552.       tickInfo.numberOfTicks = tickInfo.numberOfTicks + 5 * 
  553.                                               (tickInfo.numberOfLabels-1) + 1;
  554.       for (tick = lastLabel; 
  555.               tick + tickInfo.tickSpacing < bounds.origin.x + bounds.size.width; 
  556.               tick = tick + tickInfo.tickSpacing, tickInfo.numberOfTicks++);
  557.                                                             
  558.     return self;
  559. }
  560.  
  561. - setFrame:(const NXRect *)frameRect
  562. {
  563.     NXSize imageSize;
  564.     NXRect saveBounds;
  565.     
  566.     saveBounds = bounds;
  567.     [super setFrame:frameRect];
  568.     
  569.     [self scale:bounds.size.width/saveBounds.size.width :1];
  570.     [self translate:bounds.origin.x - saveBounds.origin.x 
  571.                    :bounds.origin.y - saveBounds.origin.y];
  572.     if (orientation == HORIZONTAL)
  573.     {
  574.         if (frameRect->size.width < frameRect->size.height)
  575.         {
  576.             orientation = VERTICAL;
  577.             [self rotate:90];
  578.             [self translate:bounds.origin.x - saveBounds.origin.x
  579.                            :bounds.origin.y + saveBounds.origin.y];
  580.         }
  581.     }
  582.     else
  583.         if (frameRect->size.width >= frameRect->size.height)
  584.         {
  585.             orientation = HORIZONTAL;
  586.             [self rotate:-90];
  587.         }
  588.             
  589.     printf("bounds %f %f %f %f\n", bounds.origin.x, bounds.origin.y,
  590.                 bounds.size.width, bounds.size.height);
  591.                     
  592.     imageSize = bounds.size;
  593.     [self convertSize:&imageSize toView:nil];
  594.     
  595.     [numberLineNXImage free];
  596.     numberLineNXImage = [[NXImage allocFromZone:[self zone]] 
  597.                                         initSize:&imageSize];
  598.     [self calculateTicks];
  599.     [self drawIntoImage];
  600.     [self display];
  601.         
  602.     return self;
  603. }
  604.  
  605. - read:(NXTypedStream *)typedStream
  606. {
  607.     [super read:typedStream];
  608.  
  609.     NXReadTypes(typedStream, "iifffff", &orientation, 
  610.                 &labelPosition, &linePosition, &backgroundGray, &lineGray, 
  611.                 &labelGray, &borderWidth, &tickInfo); 
  612.     numberLineNXImage =  NXReadObject(typedStream);
  613.     
  614.     return self;
  615. }
  616.  
  617. - write:(NXTypedStream *)typedStream
  618. {
  619.    [super write:typedStream];
  620.  
  621.     NXWriteTypes(typedStream, "iifffff", &orientation, 
  622.                 &labelPosition, &linePosition, &backgroundGray, &lineGray, 
  623.                 &labelGray, &borderWidth, &tickInfo); 
  624.     NXWriteObject(typedStream, numberLineNXImage);
  625.  
  626.    return self;
  627. }
  628.  
  629. - awake
  630. {
  631.     [super awake];
  632.     [self calculateTicks];
  633.     
  634.     return self;
  635. }
  636.  
  637. - (const char*)inspectorName
  638. {
  639.     return "NumberLineInspector";
  640. }
  641.  
  642. float mantissa(float floatValue)
  643. {
  644.     return floatValue / pow(10, orderOfMag(floatValue));
  645. }
  646.  
  647. int orderOfMag(float floatValue)
  648. {
  649.     char stringValue[30], *ePointer;
  650.     
  651.     sprintf(stringValue, "%e", floatValue);
  652.     ePointer = strchr(stringValue, 'e');
  653.     return atoi(ePointer + 1);
  654. }
  655.  
  656. int round(float floatValue)  
  657. {
  658.     return (int)(floatValue + 0.5);
  659. }
  660.  
  661. @end
  662.